/**
 * 
 */
package dh.abs.info;


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dh.abs.SuspendException;
import dh.abs.Suspendable;

/**
 * This class represents the one information on this simulation.
 * Kind of information are specified by only id.
 * There may be many instances which contain same id, because each instance hold
 * diffusion path.
 * @author denialhumanity
 *
 */
public class Information implements Suspendable {
	/**	kind of information */
	private String id;
	/** diffusion path of this information */
	private List<InformationDiffusionAgent> path;
	
	/**
	 * Default constructor. This constructor is not used.
	 */
	@SuppressWarnings("unused")
	private Information() {
	}
	
	/**
	 * This constructor creates the instance which is held by specific agent.
	 * @param id kind of information
	 * @param agent agent that holds this information initially
	 */
	public Information(String id, InformationDiffusionAgent agent) {
		this.id = id;
		path = new ArrayList<InformationDiffusionAgent>();
		path.add(agent);
	}

	/**
	 * This constructor create the instance which is loaded from files.
	 * @param id kind of information
	 * @param path information diffusion path 
	 */
	public Information(String id, List<InformationDiffusionAgent> path) {
		this.id = id;
		this.path = new ArrayList<InformationDiffusionAgent>();
		this.path.addAll(path);
	}
	
	/**
	 * This method transfer this information to target agent.
	 * @param agent
	 */
	public void transfer(InformationDiffusionAgent agent) {
		Information info = new Information(id, path);
		info.path.add(agent);
		agent.addInformation(info);
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Information other = (Information) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#getID()
	 */
	public String getID() {
		return id;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		for(InformationDiffusionAgent agent: path) {
			sb.append(agent.getID() + " ");
		}
		
		return sb.toString();
	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#storeToFile(java.io.File)
	 */
	public void storeToFile(File file) throws SuspendException {
		BufferedWriter bw = null;
		Exception exception = null;
		try {
			bw = new BufferedWriter(new FileWriter(file,true));
			bw.append(getID() + ":");
			bw.append(this.toString());
			bw.newLine();
		} catch (IOException e) {
			exception = e;
			
		} finally {
			if(bw != null) {
				try {
					bw.close();
				} catch (IOException e) {
					exception = e;
				}
			}
		}
		
		if(exception != null) {
			throw new SuspendException("Failed to store data to " + file.getAbsolutePath(), exception);
		}
	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#loadFromFile(java.io.File)
	 */
	public void loadFromFile(File file) throws SuspendException {
		//Nothing to do
	}
	
	/**
	 * This method calculates influence of agents.
	 * @return
	 */
	public Map<InformationDiffusionAgent, Double> getInfluence() {
		Map<InformationDiffusionAgent, Double> inflMap = new HashMap<InformationDiffusionAgent, Double>();
		
		for(int i = path.size() - 2; i >= 0; i--) {
			InformationDiffusionAgent agent = path.get(i);
			Double tmp = 0.0;
			
			if(inflMap.containsKey(agent)) {
				tmp = inflMap.remove(agent);
			}
			
			tmp += 1.0 / (path.size() - i - 1);
			
			inflMap.put(agent, tmp);
		}
		
		return inflMap;
	}
	
}
